package jpb;

import java.io.Serializable;


/**
 * Represents a quantity of memory in some memory unit (bits, bytes,
 * KBytes etc.).
 * 
 * @author Dariusz Kordonski
 */
public final class MemArg implements Serializable, Comparable<MemArg> {

    private static final long serialVersionUID = -3490150355604224968L;

    /**
	 * Static factory using an intermediary builder class to construct
	 * target instance of <code>MemArg</code>.<br>
	 * <br>
	 * Example usage: <code>MemArg.of(30).kiloBytes()</code>
	 * 
	 * @param q - target quantity
	 * @return new instance of {@link MemArgQuantity} to use to build a target 
	 * instance of <code>MemArg</code>
	 */
	public static MemArgQuantity of(long q) {
		return new MemArgQuantity(q);
	}
	
	/**
	 * For intuitive semantics of building {@link MemArg}
	 * by clients.
	 * 
	 * @author Dariusz Kordonski
	 */
	public static final class MemArgQuantity {
		private final long quantity;
		private MemArgQuantity(long q) {
			this.quantity = q;
		}
		private MemArg build(MemUnit u)	{ return new MemArg(quantity, u); } 
		public MemArg bits() 		{ return build(MemUnit.BITS); }
		public MemArg bytes() 		{ return build(MemUnit.BYTES); }
		public MemArg kiloBytes() 	{ return build(MemUnit.KILOBYTES); }
		public MemArg megaBytes() 	{ return build(MemUnit.MEGABYTES); }
		public MemArg gigaBytes() 	{ return build(MemUnit.GIGABYTES); }
		public MemArg teraBytes() 	{ return build(MemUnit.TERABYTES); }
	}
	
	private final long quantity;
	private final MemUnit unit;
	
	/**
	 * 
	 * 
	 * @param q - quantity
	 * @param u - unit
	 */
	private MemArg(long q, MemUnit u) {
		this.quantity = q;
		this.unit = u;
	}
	
	/* ------------------------------------------------- Data ------------------------------------------------------ */
	/**
	 * 
	 * 
	 * @return quantity of this memory argument
	 */
	public final long quantity() {
	    return this.quantity;
	}
	
	/**
	 * 
	 * 
	 * @return memory unit of this memory argument
	 */
	public final MemUnit unit() {
	    return this.unit;
	}
	
	
	/* -------------------------------------------- Conversion API ------------------------------------------------- */
	
	/**
	 * Convert this memory argument to bits.
	 * 
	 * @return new instance of <code>MemArg</code> in bits
	 */
	public MemArg toBits() {
		return to(MemUnit.BITS);
	}
	
	/**
	 * Convert this memory argument to bytes.
	 * 
	 * @return new instance of <code>MemArg</code> in bytes
	 * 
	 */
	public MemArg toBytes() {
		return to(MemUnit.BYTES);
	}
	
	/**
	 * Convert this memory argument to kilobytes.
	 * 
	 * @return new instance of <code>MemArg</code> in kilobytes
	 */
	public MemArg toKiloBytes() {
		return to(MemUnit.KILOBYTES);
	}
	
	/**
	 * Convert this memory argument to megabytes.
	 * 
	 * @return new instance of <code>MemArg</code> in megabytes
	 */
	public MemArg toMegaBytes() {
		return to(MemUnit.MEGABYTES);
	}
	
	/**
	 * Convert this memory argument to gigabytes.
	 * 
	 * @return new instance of <code>MemArg</code> in gigabytes
	 */
	public MemArg toGigaBytes() {
		return to(MemUnit.GIGABYTES);
	}
	
	/**
	 * Convert this memory argument to terabytes.
	 * 
	 * @return new instance of <code>MemArg</code> in terabytes
	 */
	public MemArg toTeraBytes() {
		return to(MemUnit.TERABYTES);
	}
	
	
	public MemArg to(MemUnit u) {
		if (this.unit == u) {
			return this;
		} else {
			return new MemArg(u.convert(quantity, unit), u);
		}
	}

	
	
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof MemArg)) {
            return false;
        }
        MemArg that = (MemArg) obj;
        return this.quantity == that.quantity && this.unit == that.unit;
    }

    @Override
    public int hashCode() {
        return (int) (17 * quantity + unit.hashCode());
    }

    @Override
    public String toString() {
        return "MemArg[quantity=" + quantity + ",unit=" + unit + "]";
    }

    
    @Override
    public int compareTo(MemArg other) {
        // TODO find lower unit, convert
        return 0;
    }
    
}
